package com.example.ReactorModel;

import com.example.util.Logger;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MultiThreadEchoHandler implements Runnable{

    final SocketChannel channel;
    final SelectionKey sk;
    final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    static final int RECIEVING = 0,SENDING =1;
    int state = RECIEVING;

    //引入线程池
    static ExecutorService pool = Executors.newFixedThreadPool(4);

    //构造器
    MultiThreadEchoHandler(Selector selector,SocketChannel c) throws IOException {
        channel = c;
        c.configureBlocking(false);
        //取得选择键，再设置感兴趣的IO事件
        sk = channel.register(selector,0);
        //将本Handler作为sk选择键的附件，方便事件dispath
        sk.attach(this);
        //向sk选择键设置Read就绪事件
        sk.interestOps(SelectionKey.OP_READ);
        selector.wakeup();
    }

    @Override
    public void run() {
        //提交数据传输任务到线程池
        //使得IO处理不在IO事件轮询线程中执行，而是在独立的线程池中执行
        pool.submit(() -> asyncRun());
    }

    //数据传输与业务处理任务，不在IO轮询线程中执行，而是在独立的线程池中执行
    public synchronized void asyncRun(){
        try {
            if(state == SENDING){
                //写入通道
                channel.write(byteBuffer);
                //byteBuffer切换成写模式，写完后准备从通道读
                byteBuffer.clear();
                //写完后，注册read就绪事件
                sk.interestOps(SelectionKey.OP_READ);
                //进入接收到状态
                state = RECIEVING;
            }else if(state == RECIEVING){
                //从通道读
                int length = 0;
                while ((length = channel.read(byteBuffer)) > 0){
                    Logger.info(new String(byteBuffer.array(),0,length));
                }
                //读完后，反转byteBuffer的读写模式
                byteBuffer.flip();
                //注册write事件
                sk.interestOps(SelectionKey.OP_WRITE);
                //进入发送的状态
                state = SENDING;
            }
        }catch (IOException ex){
            ex.printStackTrace();
        }
    }
}
